package com.msy.sign;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.web3j.crypto.ECDSASignature;
import org.web3j.crypto.Hash;
import org.web3j.crypto.Keys;
import org.web3j.crypto.Sign;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Numeric;

import java.math.BigInteger;
import java.util.Arrays;

/**
 * @ClassName MySign
 * @Description TODO
 * @Author 13683
 * @Date 2019/8/19 14:07
 * @Version 1.0
 **/
public class MySign {
    private static final Logger logger = LoggerFactory.getLogger(MySign.class);
    private static final String URL = "http://localhost:8545";

    public static void main(String[] args){
        //签名
        String signature = "0x6cdd0a72c3a6582d83c2b08590f3ec51179658d7f6f8f6ccf6c6448956576ab54ca67e4a0b09458869ef819184fe7f8672d7999dcf0c2254291a2a9b9ceb75e51c";
        Web3j web3 = Web3j.build(new HttpService(URL));
        //账户1的地址
        String address = web3.ethAccounts().flowable().blockingFirst().getAccounts().get(0);
        String resource = "123";
        boolean result = verifySignature(address,resource,signature);
        System.out.println(result);
    }

    /**
     * 检验以太坊签名是否合法
     * 先通过signature 解码出v,r,s 签名数据，再求出公钥，再求出地址并校验
     * @param from  发送者地址
     * @param resource  被签名的原数据
     * @param signature 签名
     * @return
     */
    public static boolean verifySignature(String from,String resource,String signature){

        //  这里一个字符都不能少，eth签名规则必须拼上 "\u0019Ethereum Signed Message:\n"
        String prefix = "\u0019Ethereum Signed Message:\n" + resource.length();
        byte[] msgHash = Hash.sha3((prefix + resource).getBytes());

        byte[] signatureBytes = Numeric.hexStringToByteArray(signature);
        //取出v
        byte v = signatureBytes[64];
        if (v < 27) {
            v += 27;
        }
        //取出r,s,和v一起可以求出公钥
        Sign.SignatureData sd = new Sign.SignatureData(
                v,
                Arrays.copyOfRange(signatureBytes, 0, 32),
                Arrays.copyOfRange(signatureBytes, 32, 64));

        String addressRecovered = null;
        boolean match = false;
        // 遍历recId
        for (int i = 0; i < 4; i++) {
            //计算公钥
            BigInteger publicKey = Sign.recoverFromSignature(
                    (byte) i,
                    new ECDSASignature(new BigInteger(1, sd.getR()), new BigInteger(1, sd.getS())),
                    msgHash);
            if (publicKey != null) {
                //通过公钥计算地址
                addressRecovered = "0x" + Keys.getAddress(publicKey);
                if (addressRecovered.equals(from)) {
                    match = true;
                    break;
                }
            }
        }

        logger.info("verify:"+resource +" : "+signature +" ==> "+addressRecovered);
        return match;
    }
}
